# 创建项目相关接口
from flask import Blueprint, request, send_file,session
import shutil
import time, yaml
import os
import json
import xml.etree.ElementTree as ET
from getYamlFile import get_name
from Logger import logger
from getYamlFile import getyaml
from utils import MyResponse, get_global_config, extract_compressed_file, limit_files, handle_exception, handle_success, \
     get_files_info, compress_directory_to_zip,get_files_info_db
from db_handler import db_handler
from datetime import datetime

newProjecct = Blueprint('newProjecct', __name__)

@newProjecct.before_request
def before_request():
    if 'userid' not in session:
        return handle_exception("未登录", "用户未登录，请先登录")

# 上传脚本
@newProjecct.route('/file/upload', methods=['POST'])
def upload_file():
    try:
        file = request.files['file']
        logger.info("upload filename：%s", file.filename)
        root_path = get_global_config()['rootPath']
        upload_path = os.path.join(root_path, "uploadFile")
        if not os.path.exists(root_path):
            return handle_exception("根路径错误", "全局配置错误，请联系管理员进行配置")
        if not os.path.exists(upload_path):
            os.makedirs(upload_path)
        # 保存文件
        file_name = time.strftime("%Y%m%d%H%M%S", time.localtime()) + "_" + file.filename
        file_path = os.path.join(upload_path, file_name)
        limit_files(upload_path)  # 清理
        file.save(file_path)
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        logger.info("compressed filename：%s", file_name)
        return handle_success({"fileName": file_name}, "上传成功")


# 创建项目
@newProjecct.route('/file/creatProject', methods=['POST'])
def creat_project():
    try:
        params = request.json
        project_name = params["projectName"]
        global_config = get_global_config()
        root_path = global_config['rootPath']
        jmeter_path = global_config['jmeterPath']
        jm_version = global_config['jmVersion']
        creater = params["creater"]
        if not os.path.exists(root_path) or not os.path.exists(jmeter_path):
            return handle_exception("根路径错误", "全局配置错误，请联系管理员进行配置")
        project_dir = os.path.join(root_path, project_name)
        jmx_dir = os.path.join(project_dir, "testplans")
        email_config_dir = os.path.join(project_dir, "config")
        email_template_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static','config.ini')  # 邮件模板位置

        os.makedirs(jmx_dir)  # 创建脚本目录
        os.makedirs(email_config_dir)  # 创建邮件存放目录

        zip_path = os.path.join(root_path, "uploadFile", params["fileName"])
        extract_path = os.path.join(root_path, params["projectName"], "testplans")
        extract_compressed_file(zip_path, extract_path)  # 将脚本解压缩到项目根路径
        if len(os.listdir(extract_path)) == 0:  # 判断项目路径下有无脚本
            shutil.rmtree(project_dir)
            logger.error("脚本解压缩失败！")
            return handle_exception("error", "因未检测到项目脚本，项目创建失败")

        if zip_path.lower().endswith('.zip'):
            files = os.listdir(extract_path)
            has_jmx = False
            for file in files:
                if file.endswith(".jmx"):
                    has_jmx = True
                    break  # 找到一个 .jmx 文件后就可以退出循环
            if not has_jmx:
                shutil.rmtree(project_dir)  # 删除项目文件夹
                logger.error("脚本获取失败，请检查压缩包是否符合要求")
                return handle_exception("error", "项目创建失败，请检查压缩包是否嵌套目录")

        shutil.copy(email_template_path, email_config_dir)  # 创建邮件
        yaml_file_path = os.path.join(project_dir, project_name) + '.yaml'
        getyaml(jmeter_path, jm_version, project_dir, jmx_dir, yaml_file_path)  # 生成yaml文件
        jmx_files = [f for f in os.listdir(extract_path) if f.endswith(".jmx")] # 过滤出JMX文件
        all_files=[f for f in os.listdir(extract_path)]
        delete_project_dir = False  # 是否删除项目目录

        # 检查每个 .jmx 文件的文件名是否以 "TC" 开头
        for filename in jmx_files:
            if not filename.startswith("TC"):
                delete_project_dir = True
                break
        # 删除 project_dir 目录及其子目录
        if delete_project_dir:
            shutil.rmtree(project_dir)
            return handle_exception("error", "创建失败，脚本需要以TC为前缀")

        # PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 根目录
        # project_info_path = os.path.join(PROJECT_ROOT, 'Solo\static\ProjectInfo.json')
        # # 检查文件是否存在，不存在则创建空文件
        # if not os.path.exists(project_info_path):
        #     with open(project_info_path, 'w') as file:
        #         file.write("[]")
        #
        # with open(project_info_path, 'r') as file:
        #     data = json.load(file)
        #
        # for record in data:
        #     if record["projectname"] == project_name:
        #         record["creater"] = creater  # 覆盖现有记录
        #         break
        # else:
        #     new_record = {"projectname": project_name, "creater": creater}
        #     data.append(new_record)  # 添加新的记录
        #
        # # 将更新后的数据写入文件中
        # with open(project_info_path, 'w') as file:
        #     json.dump(data, file, indent=4)




    except FileExistsError:
        return handle_exception("error", "项目名已存在")
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        # 插入项目信息到数据库并获取 project_id
        user_id = session['userid']  # 从session中获取userid
        creation_date = datetime.now().strftime('%Y-%m-%d')  # 获取当前日期作为创建日期
        #creater = session['loginname']

        # 获取所有 slaver 的 IP 地址，并按逗号分隔
        select_ip_sql = "SELECT GROUP_CONCAT(ip SEPARATOR ',') AS slaver_ips FROM slaver"
        slaver_ip_result = db_handler.execute_query(select_ip_sql)
        slaver_ip = slaver_ip_result[0]['slaver_ips'] if slaver_ip_result else None
        insert_project_sql = "INSERT INTO Project (project_name, user_id, creater, slaver_ip) VALUES (%s, %s, %s, %s)"
        db_handler.insert(insert_project_sql, (project_name, user_id, creater, slaver_ip))

        project_id = db_handler.execute_query("Select id from project where project_name = %s",(project_name,))[0]["id"]  # 获取最后插入行的ID
        # 插入脚本信息到数据库
        for file in all_files:
            file_path = os.path.join(extract_path, file)
            file_size = os.path.getsize(file_path)
            script_type = '1' if file.endswith('.jmx') else '0'
            modify_time = datetime.fromtimestamp(os.path.getmtime(file_path))
            insert_script_sql = "INSERT INTO Script (project_id, script_type, file_name, file_size, modify_time) VALUES (%s, %s, %s, %s, %s)"
            db_handler.insert(insert_script_sql, (project_id, script_type, file, file_size, modify_time))
            # 获取用户有权限的项目列表

        project_query = "SELECT project_name FROM project WHERE user_id = %s OR (SELECT is_admin FROM User WHERE id = %s) = 1"
        project_list = db_handler.execute_query(project_query, (session['userid'], session['userid']))
        project_names = [project['project_name'] for project in project_list] if project_list else []
        session['projects'] = project_names  #更新会话中的project
        return handle_success("项目创建成功", "success")

# 删除项目（管理员权限）
@newProjecct.route('/file/delProject', methods=['POST'])
def del_project():
    try:
        params = request.json
        projectName=params["projectName"]
        root_path = get_global_config()['rootPath']
        current_user = session['loginname']  # 当前操作人
        project_path = os.path.join(root_path, projectName)
        print(project_path)
        shutil.rmtree(project_path)

        delete_script="DELETE FROM script where project_id= (select id from project where project_name= %s)"
        delete_project = "DELETE FROM project WHERE project_name = %s "
        delete_params=(projectName,)
        db_handler.delete(delete_script,delete_params)
        db_handler.delete(delete_project, delete_params)
        logger.info(f"操作人：{current_user}：成功删除项目{projectName}.")

        # 获取用户有权限的项目列表
        project_query = "SELECT project_name FROM project WHERE user_id = %s OR (SELECT is_admin FROM User WHERE id = %s) = 1"
        project_list = db_handler.execute_query(project_query, (session['userid'], session['userid']))
        project_names = [project['project_name'] for project in project_list] if project_list else []
        session['projects'] = project_names
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success("success", "成功删除")

# 获取项目的创建者
# 参数：/file/getCreater
# 传参："data": "chenman"
@newProjecct.route('/file/getCreater', methods=['POST'])
def get_creater():
    try:
        params = request.json
        project_name = params["projectName"]
        query_sql="SELECT creater FROM project p WHERE project_name = %s"
        creater=db_handler.execute_query(query_sql,(project_name,))[0]['creater']
        # PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 根目录
        # project_info_path = os.path.join(PROJECT_ROOT, 'Solo\static\ProjectInfo.json')
        #
        # with open(project_info_path, 'r') as file:
        #     data = json.load(file)
        #
        # creater = ''
        # for item in data:
        #     if item.get("projectname") == project_name:
        #         creater= item.get("creater")
        #         break

    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success(creater, "success")


# 获取项目的脚本文件名
@newProjecct.route('/file/getJmxName', methods=['POST'])
def get_jmx_name():
    try:
        params = request.json
        root_path = get_global_config()['rootPath']
        project_dir = os.path.join(root_path, params["projectName"])
        jmx_list = get_name(os.path.join(root_path, params["projectName"], "testplans"), "jmx") #过滤出JMX文件
        jmx_name = [item for item in jmx_list if item.startswith("TC")] # 只要以TC开头的文件
        yaml_path = os.path.join(project_dir, params["projectName"] + '.yaml')

        with open(yaml_path, 'r', encoding='utf-8') as file:
            yaml_data = yaml.safe_load(file)

        scenarios = [item['scenario'] for item in yaml_data['execution']]

        jmx_name_filtered = [item for item in jmx_name if item.split('.')[0] not in scenarios]

    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success(jmx_name_filtered, "success")

# 获取项目的JMX文件的信息
@newProjecct.route('/file/getJmxInfo', methods=['POST'])
def get_jmx_info():
    try:
        params = request.json
        # root_path = get_global_config()['rootPath']
        project_name = params["projectName"]
        if not project_name:
            return handle_exception("error", "项目名称不能为空")

        name_filter = params.get("name", None)  # 前端传递的搜索名称，可能为空

        # 获取所有符合条件的jmx文件信息
        jmx_list = get_files_info_db(project_name, "1")
        jmx_filtered = [item for item in jmx_list if item["name"].startswith("TC")]



        # 如果存在搜索名称，进行模糊搜索过滤
        if name_filter:
            jmx_filtered = [item for item in jmx_filtered if name_filter in item["name"]]

        # 分页参数
        pageNum = int(params.get("pageNum", 1))  # 默认第一页
        pageSize = int(params.get("pageSize", 20))  # 默认每页20条

        # 计算分页的起始和结束索引
        start_index = (pageNum - 1) * pageSize
        end_index = start_index + pageSize

        # 分页后的数据
        paginated_data = jmx_filtered[start_index:end_index]

        # 构造返回结果
        result = {
            "list": paginated_data,
            "total": len(jmx_filtered)  # 总条目数，根据过滤后的列表长度确定
        }
    except Exception as err:
        return handle_exception(err, "程序异常")
    else:
        return handle_success(result, "success")

# 获取项目的参数化文件的信息
@newProjecct.route('/file/getParamsFilesInfo', methods=['POST'])
def get_params_files_info():
    try:
        params = request.json
        # root_path = get_global_config()['rootPath']
        # csv_list = get_files_info(os.path.join(root_path, params["projectName"], "testplans"), "csv")
        # txt_list = get_files_info(os.path.join(root_path, params["projectName"], "testplans"), "txt")
        # dat_list = get_files_info(os.path.join(root_path, params["projectName"], "testplans"), "dat")
        #
        # # 合并三个列表
        # params_list = []
        # params_list.extend(csv_list)
        # params_list.extend(txt_list)
        # params_list.extend(dat_list)

        # 组合结果
        # result = params_list

        project_name = params["projectName"]
        result = get_files_info_db(project_name,"0")
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success(result, "success")

# yaml文件新增sencrio
def add_scenario_to_yaml(yaml_path, testcase_name):
    with open(yaml_path, 'r', encoding='utf-8') as f:
        yaml_data = yaml.load(f, Loader=yaml.FullLoader)

    modifications = {
        'disable': ["Res_Memory", "Res_CPU"],
        'set-prop': {
            'Errors Writer>filename': f"${{Jtl_DIR}}/{testcase_name}_error.jtl",
            'KPI Writer>filename': f"${{Jtl_DIR}}/{testcase_name}_kpi.jtl",
            'Res_CPU>filename': f"${{Jtl_DIR}}/{testcase_name}_cpu.jtl",
            'Res_Memory>filename': f"${{Jtl_DIR}}/{testcase_name}_memory.jtl",
            'Runtime Controller>RunTime.seconds': f"${{Runtime}}",
            'XMLJTL>filename': f"${{Jtl_DIR}}/{testcase_name}.jtl",
            f"{testcase_name}>TransactionController.parent": False,

        }
    }

    new_scenario = {
        'modifications': modifications,
        'script': f"${{testplans_DIR}}/{testcase_name}.jmx"
    }

    yaml_data['scenarios'][testcase_name] = new_scenario

    with open(yaml_path, 'w', encoding='utf-8') as f:
        yaml.dump(yaml_data, f, allow_unicode=True)

# 上传文件，包括参数化文件和脚本文件
@newProjecct.route('/file/uploadJmx', methods=['POST'])
def upload_jmx_file():
    try:
        files = request.files.getlist('file')  # 获取上传的多个文件
        params = request.form
        project_name = params["projectName"]
        root_path = get_global_config()['rootPath']
        upload_path = os.path.join(root_path, params["projectName"], "testplans")
        if not os.path.exists(root_path):
            return handle_exception("根路径错误", "全局配置错误，请联系管理员进行配置")
        if not os.path.exists(upload_path):
            os.makedirs(upload_path)

        uploaded_files = []  # 存储成功上传的文件路径

        for file in files:
            if file.filename.endswith((".jmx", ".txt", ".csv", ".dat")):  # 检查文件后缀
                if file.filename.endswith(".jmx") and not file.filename.startswith("TC"):
                    res = MyResponse({"err": "文件命名不符合要求"}, False, "JMX文件名需以TC开头").post()
                    return res
                elif os.path.exists(os.path.join(upload_path, file.filename)):
                    return handle_exception({"err": "文件名重复"}, "文件名已存在")
                else:
                    file_path = os.path.join(upload_path, file.filename)
                    file.save(file_path)
                    uploaded_files.append(file_path)
                    logger.info("file upload：%s", file_path)

                    # 获取文件大小
                    file_size = os.path.getsize(file_path)
                    # 获取当前时间作为文件上传时间
                    modify_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
                    script_type = '1' if file.filename.endswith('.jmx') else '0'
                    project_id = db_handler.execute_query("select id from project where project_name= %s",(project_name,))[0]["id"]
                    insert_script_sql = "INSERT INTO script (project_id, script_type,file_name, file_size, modify_time) VALUES (%s, %s, %s, %s, %s)"
                    db_handler.insert(insert_script_sql, (project_id, script_type, file.filename,file_size, modify_time))

                    # yaml同步新增scenario，没有新增execution，execution由测试策略配置自由新增
                    yaml_path = os.path.join(root_path, params["projectName"], params["projectName"] + '.yaml')
                    scenario_name = file.filename.split('.')[0]
                    add_scenario_to_yaml(yaml_path, scenario_name)

            else:
                res = MyResponse({"err": "文件格式不正确"}, False, "文件格式不支持").post()
                return res

        res = MyResponse({"filePaths": uploaded_files}, True, "上传成功").post()
        return res
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")


# 删除JMX文件
@newProjecct.route('/file/delJmx', methods=['POST'])
def del_jmx_file():
    try:
        params = request.json
        root_path = get_global_config()['rootPath']
        jmx_path = os.path.join(root_path, params["projectName"], "testplans", params["fileName"])
        filename = params["fileName"].replace('.jmx', '')
        # 删除脚本
        os.remove(jmx_path)

        del_sql="delete from script where file_name= %s"
        db_handler.delete(del_sql,(params["fileName"],))
        logger.info("file delete：%s", jmx_path)

        yaml_path = os.path.join(root_path, params["projectName"], params["projectName"] + '.yaml')
        with open(yaml_path, 'r', encoding='utf-8') as file:
            yaml_data = yaml.load(file, Loader=yaml.FullLoader)

        # 删除execution中对应的内容
        yaml_data['execution'] = [e for e in yaml_data['execution'] if e['scenario'] != filename]

        yaml_data['scenarios'].pop(filename, None)

        with open(yaml_path, 'w', encoding='utf-8') as file:
            yaml.dump(yaml_data, file, allow_unicode=True)

    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success("删除成功", "success")


# 检验脚本结构，1.事务控制器名称2.资源监控插件3.参数化文件引用方式
@newProjecct.route('/file/checkJmxFile', methods=['POST'])
def check_jmx_file():
    try:
        params = request.json
        print("params:", params)
        root_path = get_global_config()['rootPath']
        check_results = [""] * len(params["fileName"])  # 用于存储每个文件的检查结果

        for index, file_name in enumerate(params["fileName"]):
            check_results[index] = "【" + file_name + '】:\n'
            jmx_name = file_name.replace(".jmx", "")
            jmx_path = os.path.join(root_path, params["projectName"], "testplans", file_name)
            # 检查脚本名字是否以TC开头
            # if not jmx_name.startswith("TC"):
            #     check_results[index] += "脚本名字没有以TC开头/n"

            tree = ET.parse(jmx_path)  # 解析jmx文件
            root = tree.getroot()  # 解析jmx文件
            has_perfmon_plugin = False
            has_transaction_controller =False
            for tc in root.findall('.//TransactionController'):  # 遍历所有TransactionController节点
                testname = tc.get('testname')
                print(testname)
                if testname == jmx_name:
                    has_transaction_controller = True
                    check_results[index] += "事务控制器名字：符合\n"
                    # logger.info(f'"{file_name}" Testname "{testname}" does not match filename')
            if not has_transaction_controller:
                check_results[index] += "！！！ERROR：：没有与脚本名一致的事务控制器\n"
                    # tc.set('testname', jmx_name)  # 修改testname的值与脚本名一致
            # tree.write(jmx_path, encoding='utf-8', xml_declaration=True)

            cpu_found = False
            memory_found = False
            has_restree = False

            for element in root.findall(
                    './/kg.apc.jmeter.perfmon.PerfMonCollector'):  # 查找所有的kg.apc.jmeter.perfmon.PerfMonCollector节点
                has_perfmon_plugin = True
                testname = element.get('testname')
                if testname == 'Res_CPU':
                    cpu_found = True
                elif testname == 'Res_Memory':
                    memory_found = True
            if not has_perfmon_plugin:
                check_results[index] += "！！！ERROR：JXM文件中没有添加PerfMonCollector组件\n"
            else:
                if cpu_found and memory_found:
                    check_results[index] +='资源监控插件：符合\n'
                else:
                    check_results[index] += "！！！ERROR：JXM文件中没有添加Res_CPU或者Res_Memory\n"

            # for element in root.findall('.//ResultCollector'):  # 查找所有的ResultCollector节点
            #     has_restree = True
            #     testname = element.get('testname')
            #     # 检查 filename 是否存在并且不为空
            #     for child in element:
            #         if child.tag == 'stringProp' and child.get('name') == 'filename':
            #             filename = child.text
            #             if not filename:
            #                 check_results[index] += "！！！ERROR：'察看结果树文件名为空\n"
            #             else:
            #                 if testname != 'XMLJTL':
            #                     check_results[index] += "！！！ERROR：察看结果树命名不正确\n"
            #                 else:
            #                     check_results[index] += '察看结果树：符合\n'
            #             break
            # if not has_restree:
            #     check_results[index] += "！！！ERROR：JXM文件中没有添加察看结果树\n"

            # 遍历所有 CSVDataSet 组件
            for csv_dataset in root.findall(".//CSVDataSet"):
                # 在 CSVDataSet 组件中查找 filename 属性
                filename_elem = csv_dataset.find(".//stringProp[@name='filename']")
                if filename_elem is not None:
                    path = filename_elem.text
                    if path:
                        if os.path.isabs(path):
                            check_results[index] += "！！！ERROR：参数化文件引用方式需要使用相对引用\n"
                        else:
                            check_results[index] += "参数化文件引用方式正确\n"
                    else:
                        check_results[index] += "没有引用参数化文件\n"
                else:
                    print("未找到路径。")
    except Exception as err:  # 捕获异常
        return handle_exception(err, "程序异常")
    else:
        return handle_success(check_results, "success")

# 下载脚本
@newProjecct.route('/file/downloadJmxFile', methods=['POST'])
def download_jmx_file():
    try:
        params = request.json
        root_path = get_global_config()['rootPath']
        jmx_path = os.path.join(root_path, params["projectName"], "testplans", params["fileName"])
        res = send_file(jmx_path, as_attachment=True)
    except Exception as err:  # 捕获异常
        print(err)  # 打印异常参数
        res = MyResponse({"err": err}, False, "error").post()
        return res
    else:
        return res

@newProjecct.route('/file/downloadAllFile', methods=['POST'])
def download_all_file():
    try:
        params = request.json
        root_path = get_global_config()['rootPath']
        input_path = os.path.join(root_path, params["projectName"], "testplans")
        output_zip =  os.path.join(root_path, params["projectName"],f"{params['projectName']}_testplans.zip")
        compress_directory_to_zip(input_path, output_zip)
        res = send_file(output_zip, as_attachment=True, download_name="testplans", mimetype="application/zip")
    except Exception as err:  # 捕获异常
        print(err)  # 打印异常参数
        res = MyResponse({"err": err}, False, "error").post()
        return res
    else:
        return res
# 下载jmetr监控插件
@newProjecct.route('/file/downloadJmeterPlugins', methods=['POST'])
def download_jmeter_plugins():
    try:
        run_dir = os.path.dirname(os.path.abspath(__file__))  # 根目录
        #path = os.path.join(PROJECT_ROOT, 'Solo\static\plugins\JmeterPlugins.zip')
        path = os.path.join(run_dir, 'static', 'plugins', 'JmeterPlugins.zip')
        res = send_file(path, as_attachment=True)
    except Exception as err:  # 捕获异常
        print(err)  # 打印异常参数
        res = MyResponse({"err": err}, False, "error").post()
        return res
    else:
        return res

# 下载浏览器录制插件
@newProjecct.route('/downloadRecordingPlugins', methods=['POST'])
def download_recording_plugins():
    try:
        run_dir = os.path.dirname(os.path.abspath(__file__))  # 根目录
        #path = os.path.join(PROJECT_ROOT, 'Solo\static\plugins\chrome-extensions-master.zip')
        path = os.path.join(run_dir, 'static', 'plugins', 'chrome-extensions-master.zip')
        res = send_file(path, as_attachment=True)
    except Exception as err:  # 捕获异常
        print(err)  # 打印异常参数
        res = MyResponse({"err": err}, False, "error").post()
        return res
    else:
        return res